﻿using RabbitMQ.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace Leo.Common.RabbitMQ
{
    public class RabbitConnection : IDisposable
    {

        private IConnection connection;

        private IChannel channel;

        internal readonly RabbitConfig config;

        private bool disposable;

        public RabbitConnection(RabbitConfig config)
        {
            this.config = config;
        }


        #region 初始化

        /// <summary>
        /// 初始化
        /// </summary>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        private async Task Init()
        {
            if (this.disposable)
            {
                throw new Exception("RabbitMQ连接已经主动释放啦");
            }

            var conn = await GetConnectionAsync();
            this.channel = await conn.CreateChannelAsync();
        }


        #endregion



        #region Connection 连接


        /// <summary>
        /// 获取连接
        /// </summary>
        /// <returns></returns>
        private async Task<IConnection> GetConnectionAsync()
        {
            if (this.connection == null || !this.connection.IsOpen)
            {
                this.connection = await this.CreateConnectionAsync();
            }

            return this.connection;
        }

        /// <summary>
        /// 创建Rabbit连接
        /// </summary>
        /// <returns></returns>
        public async Task<IConnection> CreateConnectionAsync()
        {
            ConnectionFactory connectionFactory = new ConnectionFactory
            {
                HostName = this.config.Host,
                Port = this.config.Port,
                UserName = this.config.UserName,
                Password = this.config.Password,
                AutomaticRecoveryEnabled = true,
                TopologyRecoveryEnabled = true,
                NetworkRecoveryInterval = TimeSpan.FromSeconds(10.0),
            };

            RabbitConfig rabbitConfig = this.config;

            if (rabbitConfig.ConnectionName == null)
            {
                rabbitConfig.ConnectionName = GetProvidedName();
            }
            if (!string.IsNullOrEmpty(this.config.ConnectionName))
            {
                connectionFactory.ClientProvidedName = this.config.ConnectionName;
            }
            if (!string.IsNullOrEmpty(this.config.VirtualHost))
            {
                connectionFactory.VirtualHost = this.config.VirtualHost;
            }
            return await connectionFactory.CreateConnectionAsync();
        }


        private static string GetProvidedName()
        {
            string environmentVariable = Environment.GetEnvironmentVariable("AppName");
            if (string.IsNullOrEmpty(environmentVariable))
            {
                return null;
            }
            string hostName = Dns.GetHostName();
            return environmentVariable + "-" + hostName;
        }


        #endregion



        #region Channel 连接

        /// <summary>
        /// 获取通道
        /// </summary>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<IChannel> GetChannelAsync()
        {
            if (this.channel is null || this.channel.IsClosed)
            {
                await this.Init();
            }
            if (this.channel is null)
            {
                throw new Exception("创建RabbitMQ信道失败");
            }
            return this.channel;
        }



        /// <summary>
        /// 创建通道
        /// </summary>
        /// <returns></returns>
        internal async Task<IChannel> CreateChannelAsync()
        {
            var conn = await GetConnectionAsync();

            return await conn.CreateChannelAsync();
        }


        #endregion


        /// <summary>
        /// 声明
        /// </summary>
        /// <param name="rabbitObjectBase"></param>
        public void Declare(RabbitObjectBase rabbitObjectBase)
        {
            RabbitQueueInfo rabbitQueueInfo = rabbitObjectBase as RabbitQueueInfo;
            if (rabbitQueueInfo != null)
            {
                this.DeclareQueue(rabbitQueueInfo);
                return;
            }

            RabbitExchangeInfo rabbitExchangeInfo = rabbitObjectBase as RabbitExchangeInfo;
            if (rabbitExchangeInfo != null)
            {
                this.DeclareExchange(rabbitExchangeInfo, true);
            }
        }


        /// <summary>
        /// 声明队列
        /// </summary>
        /// <param name="rabbitQueueInfo"></param>
        private void DeclareQueue(RabbitQueueInfo rabbitQueueInfo)
        {
            channel.QueueDeclareAsync(rabbitQueueInfo.Name, rabbitQueueInfo.Durable, rabbitQueueInfo.Exclusive, rabbitQueueInfo.AutoDelete, rabbitQueueInfo.Properties);

        }

        /// <summary>
        /// 声明交换机
        /// </summary>
        /// <param name="rabbitExchangeInfo"></param>
        /// <param name="deleteWhileConflict"></param>
        private void DeclareExchange(RabbitExchangeInfo rabbitExchangeInfo, bool deleteWhileConflict = true)
        {
            channel.ExchangeDeclareAsync(rabbitExchangeInfo.Name, "", rabbitExchangeInfo.Durable, rabbitExchangeInfo.AutoDelete, rabbitExchangeInfo.Properties);

        }







        public void Dispose()
        {
            this.channel?.Dispose();
            this.connection?.Dispose();
        }
    }
}
